#pragma once
#include <sfse_common/Relocation.h>
#include <sfse_common/Types.h>

#include "ScaleformStat.h"
#include "ScaleformSysAlloc.h"
#include "ScaleformTypes.h"

namespace Scaleform
{
    class MemoryHeap //  : public ListNode<MemoryHeap>;
    {
        enum MemReportType
        {
            MemReportBrief,
            MemReportFull,
            MemReportFileSummary,
            MemReportHeapDetailed
        };

        enum HeapFlags
        {
            Heap_ThreadUnsafe = 0x0001,
            Heap_FastTinyBlocks = 0x0002,
            Heap_FixedGranularity = 0x0004,
            Heap_Root = 0x0008,
            Heap_NoDebugInfo = 0x0010,
            Heap_UserDebug = 0x1000
        };

        enum RootHeapParameters
        {
            RootHeap_MinAlign = 16,
            RootHeap_Granularity = 16 * 1024,
            RootHeap_Reserve = 16 * 1024,
            RootHeap_Threshold = 256 * 1024,
            RootHeap_Limit = 0
        };

        struct HeapDesc
        {
            unsigned Flags;
            UPInt MinAlign;
            UPInt Granularity;
            UPInt Reserve;
            UPInt Threshold;
            UPInt Limit;
            UPInt HeapId;
            UPInt Arena;

            HeapDesc(unsigned flags = 0,
                     UPInt minAlign = 16,
                     UPInt granularity = 8 * 1024,
                     UPInt reserve = 8 * 1024,
                     UPInt threshold = ~UPInt(0),
                     UPInt limit = 0,
                     UPInt heapId = 0,
                     UPInt arena = 0)
                : Flags(flags), MinAlign(minAlign),
                  Granularity(granularity), Reserve(reserve),
                  Threshold(threshold), Limit(limit),
                  HeapId(heapId), Arena(arena)
            {
            }

            void Clear()
            {
                Flags = 0;
                Granularity = 0;
                MinAlign = 16;
                Reserve = 0;
                Threshold = ~UPInt(0);
                Limit = 0;
                HeapId = 0;
                Arena = 0;
            }
        };

        struct HeapInfo
        {
            HeapDesc Desc;
            MemoryHeap* pParent;
            char* pName;
        };

        struct HeapVisitor
        {
            virtual ~HeapVisitor()
            {
            }

            virtual void Visit(MemoryHeap* parentHeap, MemoryHeap* childHeap) = 0;
        };

        struct LimitHandler
        {
            virtual ~LimitHandler()
            {
            }

            virtual bool OnExceedLimit(MemoryHeap* heap, UPInt overLimit) = 0;
            virtual void OnFreeSegment(MemoryHeap* heap, UPInt freeingSize) = 0;

            int AllocCount;
        };

        struct HeapTracer
        {
            virtual ~HeapTracer()
            {
            }

            virtual void OnCreateHeap(const MemoryHeap* heap) = 0;
            virtual void OnDestroyHeap(const MemoryHeap* heap) = 0;
            virtual void OnAlloc(const MemoryHeap* heap, UPInt size, UPInt align, unsigned sid, const void* ptr) = 0;
            virtual void OnRealloc(const MemoryHeap* heap, const void* oldPtr, UPInt newSize, const void* newPtr) = 0;
            virtual void OnFree(const MemoryHeap* heap, const void* ptr) = 0;
        };

        struct RootHeapDesc : public HeapDesc
        {
        };

        struct RootStats
        {
            UPInt SysMemFootprint;
            UPInt SysMemUsedSpace;
            UPInt PageMapFootprint;
            UPInt PageMapUsedSpace;
            UPInt BookkeepingFootprint;
            UPInt BookkeepingUsedSpace;
            UPInt DebugInfoFootprint;
            UPInt DebugInfoUsedSpace;
            UPInt UserDebugFootprint;
            UPInt UserDebugUsedSpace;
        };

    protected:
        MemoryHeap();

        virtual ~MemoryHeap()
        {
        }

    public:
        virtual void CreateArena(UPInt arena, SysAllocPaged* sysAlloc) = 0;
        virtual void DestroyArena(UPInt arena) = 0;
        virtual bool ArenaIsEmpty(UPInt arena) = 0;
        virtual MemoryHeap* CreateHeap(const char* name, const HeapDesc& desc) = 0;
        virtual void SetLimitHandler(LimitHandler* handler) = 0;
        virtual void SetLimit(UPInt newLimit) = 0;
        virtual void AddRef() = 0;
        virtual void Release() = 0;
        virtual void* Alloc(UPInt size, UPInt align, const AllocInfo* info = 0) = 0;
        virtual void* Alloc(UPInt size, const AllocInfo* info = 0) = 0;
        virtual void* Realloc(void* oldPtr, UPInt newSize) = 0;
        virtual void Free(void* ptr) = 0;
        virtual void* AllocAutoHeap(const void* thisPtr, UPInt size, UPInt align, const AllocInfo* info = 0) = 0;
        virtual void* AllocAutoHeap(const void* thisPtr, UPInt size, const AllocInfo* info = 0) = 0;
        virtual MemoryHeap* GetAllocHeapOrNULL(const void* thisPtr) = 0;
        virtual UPInt GetUsableSize(const void* ptr) = 0;
        virtual void* AllocSysDirect(UPInt size) = 0;
        virtual void FreeSysDirect(void* ptr, UPInt size) = 0;
        virtual bool GetStats(StatBag* bag) = 0;
        virtual UPInt GetFootprint() const = 0;
        virtual UPInt GetTotalFootprint() const = 0;
        virtual UPInt GetUsedSpace() const = 0;
        virtual UPInt GetTotalUsedSpace() const = 0;
        virtual void GetRootStats(RootStats* stats) = 0;
        virtual void VisitMem(void* visitor, u32 unk2); // Scaleform::Heap::MemVisitor*
        virtual void VisitRootSegments(void* visitor); // Scaleform::Heap::SegVisitor*
        virtual void VisitHeapSegments(void* visitor); // Scaleform::Heap::SegVisitor*
        virtual void SetTracer(void* tracer); // Scaleform::MemoryHeap::HeapTracer*

        MemoryHeap* CreateHeap(const char* name,
                               unsigned flags = 0,
                               UPInt minAlign = 16,
                               UPInt granularity = 16 * 1024,
                               UPInt reserve = 16 * 1024,
                               UPInt threshold = ~UPInt(0),
                               UPInt limit = 0,
                               UPInt heapId = 0,
                               UPInt arena = 0)
        {
            HeapDesc desc(flags, minAlign, granularity, reserve, threshold, limit, heapId, arena);
            return CreateHeap(name, desc);
        }

    protected:
        virtual void destroyItself() = 0;
        virtual void ultimateCheck() = 0;
        virtual void releaseCachedMem() = 0;
        virtual bool dumpMemoryLeaks() = 0;
        virtual void checkIntegrity() const = 0;
        virtual void getUserDebugStats(RootStats* stats) const = 0;
    };

    class MemoryHeapPT : public MemoryHeap
    {
    };

    namespace Memory
    {
        extern RelocPtr<MemoryHeap*> pGlobalHeap;
        extern void* ScaleformHeap_Allocate(size_t size);
        extern void ScaleformHeap_Free(void* ptr);
    }
}
